package huriezy.quickcontacts.models.managers;

import huriezy.quickcontacts.models.LogDebug;
import huriezy.quickcontacts.models.interfaces.IAddressDetectionListener;
import huriezy.quickcontacts.models.interfaces.ILocationManager;

import java.io.IOException;
import java.util.List;

import android.content.Context;
import android.content.SharedPreferences;
import android.location.Address;
import android.location.Criteria;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.widget.Toast;

import com.google.android.gms.location.LocationClient;

public class ContactLocationManager implements ILocationManager {

	private final String useGPSPreference = "use_gps_pref";
	
	public static final int				CONNECTION_FAILURE_RESOLUTION_REQUEST	= 9000;
	private static final int			GEOCODER_MAX_RESULTS					= 5;
	private static final long			UPDATE_INTERVAL							= 5000;

	private ConnectionLocationListener	connectionListener;
	private IAddressDetectionListener	addressListener;
	private LocationManager				locationManager;
	private Criteria					criteria;
	private String						selectedProvider;
	private Geocoder					geocoder;
	private boolean						updateLocationStarted					= false;

	private final Context				parentContext;
	private boolean						init									= false;

	public ContactLocationManager(Context context) {
		this.parentContext = context;
	}

	/**
	 * Init all needed location detection objects.
	 */
	public void initLocationConnections() {
		init = true;
		connectionListener = new ConnectionLocationListener();
		geocoder = new Geocoder(parentContext);
		locationManager = (LocationManager) parentContext.getSystemService(Context.LOCATION_SERVICE);
		if (locationManager == null) {
			LogDebug.error("The application was unable to locate the Location manager.", parentContext);
			init = false;
		}
		else{
			SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(parentContext);
			if(preferences != null){
				boolean useGPS = preferences.getBoolean(useGPSPreference, false);
				if(useGPS){
					selectedProvider = LocationManager.GPS_PROVIDER;
				}
				else{
					criteria = new Criteria();
					criteria.setCostAllowed(false);
					criteria.setAccuracy(Criteria.ACCURACY_COARSE);
					selectedProvider = locationManager.getBestProvider(criteria, true);
				}
			}
		}
	}

	/**
	 * Ask the {@link Geocoder} object to translate a {@link Location} into a
	 * list of {@link Address}.
	 * 
	 * @param currentLocation
	 */
	public Address getAddressFromLocation(Location currentLocation) {
		Address result = null;
		LogDebug.info("Geocoder is " + geocoder + " and location is " + currentLocation, getClass());
		if (geocoder != null && currentLocation != null) {
			try {
				List<Address> listAddress = geocoder.getFromLocation(currentLocation.getLatitude(),
						currentLocation.getLongitude(), GEOCODER_MAX_RESULTS);
				if (listAddress != null && !listAddress.isEmpty()) {
					result = listAddress.get(0);
				}
				LogDebug.info("geocoder result is " + result, getClass());
			}
			catch (IOException e) {
				LogDebug.error("Cannot retrieve address list from location " + currentLocation, parentContext, e);
			}
		}
		return result;
	}

	@Override
	public boolean isProviderAvailable() {
		if (!init) {
			initLocationConnections();
		}
		return locationManager.isProviderEnabled(selectedProvider);
	}

	/**
	 * @return the current {@link Location} object.
	 */
	public void startLocationUpdates() {
		if (!init) {
			initLocationConnections();
		}
		if (!updateLocationStarted) {
			updateLocationStarted = true;
			locationManager.requestLocationUpdates(selectedProvider, UPDATE_INTERVAL, 0, connectionListener);
		}
	}

	public void stopLocationUpdates() {
		if (!init) {
			initLocationConnections();
		}
		if (updateLocationStarted) {
			locationManager.removeUpdates(connectionListener);
			updateLocationStarted = false;
		}
	}

	/**
	 * @return the last registered {@link Location}.
	 */
	public Location getLastKnownLocation() {
		Location result = null;
		if (!init) {
			initLocationConnections();
		}
		else {
			result = locationManager.getLastKnownLocation(selectedProvider);
		}
		return result;
	}

	public void setAddressDetectionListener(IAddressDetectionListener listener) {
		addressListener = listener;
	}

	private void fireAddressAdded(Address address, float accuracy) {
		if (addressListener != null) {
			addressListener.onAddressDetected(address, accuracy);
		}
	}

	/**
	 * A listener class that handles {@link LocationClient} updates.
	 */
	public class ConnectionLocationListener implements LocationListener {

		@Override
		public void onLocationChanged(Location location) {
			String msg = "Updated Location: " + Double.toString(location.getLatitude()) + ","
					+ Double.toString(location.getLongitude());
			LogDebug.info(msg, getClass());
			Address address = (Address) getAddressFromLocation(location);
			if (address != null) {
				fireAddressAdded(address, location.getAccuracy());
			}
		}

		@Override
		public void onProviderDisabled(String arg0) {
			String msg = "Connection lost. Please re-connect.";
			Toast.makeText(parentContext, msg, Toast.LENGTH_SHORT).show();
			LogDebug.info(msg, getClass());
		}

		@Override
		public void onProviderEnabled(String arg0) {
			String msg = "Connection started";
			Toast.makeText(parentContext, msg, Toast.LENGTH_SHORT).show();
			LogDebug.info(msg, getClass());
		}

		@Override
		public void onStatusChanged(String provider, int status, Bundle extras) {
			String msg = "Status changed : " + extras;
			// Toast.makeText(parentContext, msg, Toast.LENGTH_SHORT).show();
			LogDebug.info(msg, getClass());
		}
	}
}
